﻿using NModbus;
using NModbus.Serial;
using System;
using System.IO;
using System.IO.Ports;
using System.Threading;

namespace TempHumidityMonitor {
    /// <summary>
    /// Modbus RTU 通信帮助类 (适配 NModbus 3.0.81)
    /// </summary>
    public class ModbusHelper : IDisposable {
        private SerialPort _serialPort;
        private IModbusSerialMaster _modbusMaster;
        // ✅ 新增：用于控制 I/O 操作生命周期的 CancellationTokenSource
        private CancellationTokenSource _ioCts;

        public bool IsConnected => _serialPort?.IsOpen == true;

        /// <summary>
        /// 连接到Modbus设备
        /// </summary>
        /// <param name="portName">串口号，如 "COM3"</param>
        /// <param name="baudRate">波特率</param>
        /// <returns>是否连接成功</returns>
        public bool Connect(string portName, int baudRate = 9600) {
            try {
                if (_serialPort != null && _serialPort.IsOpen)
                    _serialPort.Close();

                _serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);

                // ✅ 添加这两行
                _serialPort.ReadTimeout = 1500;   // 设置读取超时为1.5秒
                _serialPort.WriteTimeout = 1000;  // 设置写入超时为1秒

                _serialPort.Open(); // 必须先打开

                var adapter = new SerialPortAdapter(_serialPort);
                var factory = new ModbusFactory();
                _modbusMaster = factory.CreateRtuMaster(_serialPort);

                // ✅ 创建新的 CTS 用于本次连接的 I/O 操作
                _ioCts = new CancellationTokenSource();

                return true;
            }
            catch (Exception ex) {
                System.Diagnostics.Debug.WriteLine($"Modbus连接失败: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 读取温度和湿度
        /// </summary>
        /// <param name="slaveId">从站地址</param>
        /// <returns>包含温度和湿度的元组，失败返回null</returns>
        public (float temperature, float humidity)? ReadData(byte slaveId = 1) {
            if (!IsConnected)
                return null;

            try {
                // ✅ 在执行任何可能阻塞的操作前，先检查是否已被取消
                _ioCts.Token.ThrowIfCancellationRequested();

                // 读取保持寄存器，起始地址0，读取2个寄存器
                ushort[] registers = _modbusMaster.ReadHoldingRegisters(slaveId, startAddress: 0, numberOfPoints: 2);

                // 假设数据格式：[温度值*10, 湿度值*10]
                float temperature = registers[0] / 10.0f;
                float humidity = registers[1] / 10.0f;

                return (temperature, humidity);
            }
            catch (OperationCanceledException) {
                // 被取消，正常退出
                throw;
            }
            catch (IOException ex) when (ex.InnerException is TimeoutException) {
                // 读取超时，可能是设备无响应
                System.Diagnostics.Debug.WriteLine("Modbus读取超时");
                return null;
            }
            catch (Exception ex) {
                System.Diagnostics.Debug.WriteLine($"Modbus读取失败: {ex.Message}");
                return null;
            }
        }

        #region IDisposable Support
        private bool disposedValue = false;
        protected virtual void Dispose(bool disposing) {
            if (!disposedValue) {
                if (disposing) {
                    // ✅ 1. 首先取消所有正在进行的 I/O 操作
                    _ioCts?.Cancel();
                    _ioCts?.Dispose();
                    _ioCts = null;
                    try {
                        _serialPort?.Dispose();
                    }
                    catch (Exception ex) {
                        System.Diagnostics.Debug.WriteLine($"释放 SerialPort 资源时出错: {ex.Message}");
                    }

                    // 现在再 Dispose Master，它内部的操作应该会因端口已关而快速失败
                    _modbusMaster?.Dispose();

                    // 最后清理 SerialPort 资源
                    _serialPort = null;
                }
                disposedValue = true;
            }
        }

        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}